<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>11、Set和Map数据结构——WeakSet</title>
	</head>
	<body>
		<script type="text/javascript">
			//二、WeakSet
			//1.含义
			//WeakSet 结构与 Set 类似，也是不重复的值的集合。但是，它与 Set 有两个区别。
			//首先，WeakSet 的成员只能是对象，而不能是其他类型的值。
//			const ws = new WeakSet();
//			console.log(ws.add(1));  //Uncaught TypeError: Invalid value used in weak set
//			console.log(ws.add(Symbol())) //Uncaught TypeError: Invalid value used in weak set
			//上面代码试图向 WeakSet 添加一个数值和Symbol值，结果报错，因为 WeakSet 只能放置对象。
			//其次，WeakSet 中的对象都是弱引用，即垃圾回收机制不考虑 WeakSet 对该对象的引用，也就是说，如果其他对象都不再引用该对象，那么垃圾回收机制会自动回收该对象所占用的内存，不考虑该对象还存在于 WeakSet 之中。
			//这是因为垃圾回收机制依赖引用计数，如果一个值的引用次数不为0，垃圾回收机制就不会释放这块内存。结束使用该值之后，有时会忘记取消引用，导致内存无法释放，进而可能会引发内存泄漏。WeakSet 里面的引用，都不计入垃圾回收机制，所以就不存在这个问题。因此，WeakSet 适合临时存放一组对象，以及存放跟对象绑定的信息。只要这些对象在外部消失，它在 WeakSet 里面的引用就会自动消失。
			//由于上面这个特点，WeakSet 的成员是不适合引用的，因为它会随时消失。另外，由于 WeakSet 内部有多少个成员，取决于垃圾回收机制有没有运行，运行前后很可能成员个数是不一样的，而垃圾回收机制何时运行是不可预测的，因此 ES6 规定 WeakSet 不可遍历。
			//这些特点同样适用于本章后面要介绍的 WeakMap 结构。
			
			
			//2.语法
			//WeakSet 是一个构造函数，可以使用new命令，创建 WeakSet 数据结构。
			const ws_1 = new WeakSet();
			//作为构造函数，WeakSet可以接受一个数组或类似数组的对象作为参数。（实际上，任何具有 Iterable 接口的对象，都可以作为 WeakSet 的参数。）该数组的所有成员，都会自动成为 WeakSet 实例对象的成员。
			const a = [[1,2],[3,4]];
			const ws_2 = new WeakSet(a);
			console.log(ws_2) //WeakSet {[1, 2], [3, 4]}
			//上面代码中，a是一个数组，它有两个成员，也都是数组。将a作为 WeakSet 构造函数的参数，a的成员会自动成为 WeakSet 的成员。
			//注意，是a数组的成员成为 WeakSet 的成员，而不是a数组本身。这意味着，数组的成员只能是对象。
//			const b = [3,4];
//			const ws_3 = new WeakSet(b);
//			console.log(ws_3)  //Uncaught TypeError: Invalid value used in weak set
			//上面代码中，数组b的成员不是对象，加入 WeaKSet 就会报错。
			//WeakSet 结构有以下三个方法。
			
//		    WeakSet.prototype.add(value)：向 WeakSet 实例添加一个新成员。
//		    WeakSet.prototype.delete(value)：清除 WeakSet 实例的指定成员。
//		    WeakSet.prototype.has(value)：返回一个布尔值，表示某个值是否在 WeakSet 实例之中。
			const ws_4 = new WeakSet();
			const obj = {};
			const foo = {};
			ws_4.add(window);
			ws_4.add(obj);
			console.log(ws_4.has(window))  //true
			console.log(ws_4.has(foo))    //false
			ws_4.delete(window)
			console.log(ws_4.has(window))   //false
			//WeakSet没有size属性，没有办法遍历它的成员。
			console.log(ws_4.size)  //undefined
			console.log(ws_4.forEach) //undefined
			
//			ws_4.forEach(function(item){
//				console.log('WeakSet has'+item) //Uncaught TypeError: ws_4.forEach is not a function
//			})
			//上面代码试图获取size和forEach属性，结果都不能成功。
			//WeakSet 不能遍历，是因为成员都是弱引用，随时可能消失，遍历机制无法保证成员的存在，很可能刚刚遍历结束，成员就取不到了。WeakSet 的一个用处，是储存 DOM 节点，而不用担心这些节点从文档移除时，会引发内存泄漏。
			const foos = new WeakSet();
			class Foo {
				constructor(){
					foos.add(this)
				}
				method(){
					if(!foos.has(this)){
						throw new TypeError('Foo.prototype.method 只能在Foo的实例上调用！')
					}
				}
			}
			//WeakSet 不能遍历，是因为成员都是弱引用，随时可能消失，遍历机制无法保证成员的存在，很可能刚刚遍历结束，成员就取不到了。WeakSet 的一个用处，是储存 DOM 节点，而不用担心这些节点从文档移除时，会引发内存泄漏。
			
		</script>
	</body>
</html>
